<!--
 * @File name: VideoPlayer Component
 * @Author: 1079161148@qq.com
 * @Version: V1.0
 * @Description: 基于 Video.js 和 Hls.js 的视频播放器组件
-->
<template>
  <div class="video-player" :class="{ 'is-loading': loading }">
    <video ref="videoRef" class="video-js vjs-big-play-centered vjs-theme-custom" />
    <div v-if="loading" class="loading-overlay">
      <div class="loading-spinner"></div>
    </div>
    <div v-if="error" class="error-overlay">
      <div class="error-content">
        <span class="error-icon">!</span>
        <p class="error-message">{{ error.message || '视频加载失败' }}</p>
        <button class="retry-button" @click="handleRetry">重试</button>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch, nextTick } from 'vue'
import videojs from 'video.js'
import Hls from 'hls.js'
import type Player from 'video.js/dist/types/player'
import 'video.js/dist/video-js.css'

// 定义环境变量类型
declare const process: {
  env: {
    NODE_ENV: string
  }
}

interface PlayerOptions {
  controls?: boolean
  fluid?: boolean
  preload?: string
  controlBar?: Record<string, any>
  html5?: {
    vhs?: {
      enableLowInitialPlaylist?: boolean
      smoothQualityChange?: boolean
      overrideNative?: boolean
    }
  }
  sources?: Array<{ src: string; type?: string }>
  poster?: string
  playbackRates?: number[]
  [key: string]: any
}

interface Props {
  /** 视频源 URL */
  src: string
  /** Video.js 配置选项 */
  options?: PlayerOptions
  /** 是否自动播放 */
  autoplay?: boolean
  /** 是否为直播流 */
  isLive?: boolean
  /** 视频封面图 */
  poster?: string
  /** 重试次数 */
  maxRetries?: number
}

const props = withDefaults(defineProps<Props>(), {
  src: '',
  options: () => ({}),
  autoplay: false,
  isLive: false,
  poster: '',
  maxRetries: 3,
})

const emit = defineEmits<{
  (e: 'ready', player: Player): void
  (e: 'error', error: { type: string; error: any }): void
  (e: 'play'): void
  (e: 'pause'): void
  (e: 'ended'): void
  (e: 'timeupdate', currentTime: number | undefined): void
  (e: 'loadedmetadata', duration: number | undefined): void
  (e: 'fullscreenchange', isFullscreen: boolean | undefined): void
}>()

const videoRef = ref<HTMLVideoElement | null>(null)
const player = ref<Player | null>(null)
const loading = ref(true)
const error = ref<{ type: string; message: string } | null>(null)
const retryCount = ref(0)
let hls: Hls | null = null

// 初始化播放器
const initializePlayer = async () => {
  // 确保之前的实例被完全清理
  if (player.value || hls) {
    await destroyPlayer()
    await nextTick()
  }

  if (!videoRef.value) {
    console.error('视频元素未找到')
    return
  }

  if (!props.src) {
    console.error('视频源未提供')
    return
  }

  loading.value = true
  error.value = null

  try {
    // 确保先销毁现有播放器
    await destroyPlayer()
    await nextTick()

    // 默认配置
    const defaultOptions: PlayerOptions = {
      controls: true,
      fluid: true,
      preload: 'auto',
      poster: props.poster,
      playbackRates: [0.5, 1, 1.25, 1.5, 2],
      html5: {
        vhs: {
          enableLowInitialPlaylist: true,
          smoothQualityChange: true,
          overrideNative: true,
        },
      },
      controlBar: {
        playToggle: true,
        volumePanel: true,
        currentTimeDisplay: !props.isLive,
        timeDivider: !props.isLive,
        durationDisplay: !props.isLive,
        progressControl: !props.isLive,
        fullscreenToggle: true,
        pictureInPictureToggle: true,
        playbackRateMenuButton: !props.isLive,
        qualitySelector: true,
        liveDisplay: props.isLive,
      },
      sources: [{ src: props.src }],
    }

    // 合并用户配置
    const finalOptions = {
      ...defaultOptions,
      ...props.options,
      autoplay: props.autoplay,
    }

    console.log('初始化播放器，视频源:', props.src)

    // 初始化 Video.js 播放器
    const newPlayer = videojs(videoRef.value, finalOptions)
    player.value = newPlayer as Player

    // 处理 HLS 流媒体
    if (typeof props.src === 'string' && props.src.includes('.m3u8')) {
      if (Hls.isSupported()) {
        hls = new Hls({
          enableWorker: true,
          debug: process.env.NODE_ENV === 'development',
          liveDurationInfinity: props.isLive,
          backBufferLength: props.isLive ? 30 : 90,
          liveSyncDurationCount: props.isLive ? 3 : 7,
          liveMaxLatencyDurationCount: props.isLive ? 5 : 10,
          maxBufferSize: props.isLive ? 10 * 1000 * 1000 : 30 * 1000 * 1000,
          maxBufferLength: props.isLive ? 10 : 30,
          startLevel: -1, // 自动选择最佳质量
        })

        hls.loadSource(props.src)
        hls.attachMedia(videoRef.value)

        hls.on(Hls.Events.MANIFEST_PARSED, () => {
          if (props.autoplay && player.value) {
            const playPromise = player.value.play()
            if (playPromise !== undefined) {
              playPromise.catch((error) => {
                console.warn('自动播放失败:', error)
                emit('error', { type: 'autoplay', error })
              })
            }
          }
        })

        hls.on(Hls.Events.ERROR, (event, data) => {
          if (data.fatal) {
            switch (data.type) {
              case Hls.ErrorTypes.NETWORK_ERROR:
                console.warn('网络错误，尝试恢复...')
                if (retryCount.value < props.maxRetries) {
                  retryCount.value++
                  hls?.startLoad()
                } else {
                  error.value = { type: 'network', message: '网络错误，请检查网络连接' }
                  emit('error', { type: 'hls', event, data })
                }
                break
              case Hls.ErrorTypes.MEDIA_ERROR:
                console.warn('媒体错误，尝试恢复...')
                hls?.recoverMediaError()
                break
              default:
                error.value = { type: 'fatal', message: '播放器发生致命错误' }
                destroyPlayer()
                break
            }
          }
          emit('error', { type: 'hls', event, data })
        })

        // 清晰度切换
        hls.on(Hls.Events.LEVEL_SWITCHED, (_, data) => {
          const levels = hls?.levels || []
          const currentLevel = levels[data.level]
          console.log('清晰度切换:', currentLevel?.height + 'p')
        })
      } else if (videoRef.value.canPlayType('application/vnd.apple.mpegurl')) {
        videoRef.value.src = props.src
      } else {
        error.value = { type: 'not_supported', message: '当前环境不支持播放 HLS 流' }
        emit('error', { type: 'hls_not_supported' })
      }
    } else if (player.value) {
      // 普通视频源
      await nextTick()
      player.value.src({ src: props.src })
    }

    // 播放器事件监听
    if (player.value) {
      const p = player.value

      p.ready(function (this: Player) {
        if (player.value) {
          console.log('播放器就绪')
          loading.value = false
          emit('ready', this)
        }
      })

      // 基础事件
      p.on('play', () => emit('play'))
      p.on('pause', () => emit('pause'))
      p.on('ended', () => emit('ended'))
      p.on('timeupdate', () => {
        const currentTime = p.currentTime()
        emit('timeupdate', currentTime)
      })
      p.on('loadedmetadata', () => {
        const duration = p.duration()
        emit('loadedmetadata', duration)
      })
      p.on('fullscreenchange', () => {
        const isFullscreen = p.isFullscreen()
        emit('fullscreenchange', isFullscreen)
      })

      // 错误处理
      p.on('error', (e: unknown) => {
        console.error('播放器错误:', e)
        const errorDetails = p.error()
        const errorCode = errorDetails?.code || 0
        error.value = {
          type: 'player',
          message: getErrorMessage(errorCode),
        }
        emit('error', { type: 'player', error: errorDetails })
      })

      // 缓冲监听
      p.on('waiting', () => {
        loading.value = true
      })

      p.on('canplay', () => {
        loading.value = false
      })

      // 销毁事件
      const removeAllListeners = () => {
        const events = [
          'play',
          'pause',
          'ended',
          'timeupdate',
          'loadedmetadata',
          'fullscreenchange',
          'error',
          'waiting',
          'canplay',
        ]
        events.forEach((event) => {
          try {
            p.off(event)
          } catch (e) {
            console.warn(`移除事件监听失败 ${event}:`, e)
          }
        })
      }

      p.on('dispose', removeAllListeners)
    }
  } catch (err: unknown) {
    const errorMessage = err instanceof Error ? err.message : '未知错误'
    console.error('初始化播放器失败:', errorMessage)
    error.value = {
      type: 'initialization',
      message: '初始化播放器失败',
    }
    emit('error', {
      type: 'initialization',
      error: errorMessage,
    })
  }
}

// 获取错误信息
const getErrorMessage = (code: number): string => {
  switch (code) {
    case 1:
      return '加载视频失败'
    case 2:
      return '网络错误'
    case 3:
      return '视频解码失败'
    case 4:
      return '视频格式不支持'
    default:
      return '未知错误'
  }
}

// 重试播放
const handleRetry = async () => {
  retryCount.value = 0
  error.value = null
  await initializePlayer()
}

// 销毁播放器
const destroyPlayer = async () => {
  try {
    // 先重置错误状态
    error.value = null

    // 先暂停所有媒体操作
    if (player.value) {
      try {
        player.value.pause()
        // 触发 dispose 事件来移除所有监听器
        player.value.trigger('dispose')
      } catch (e) {
        console.warn('暂停播放器失败:', e)
      }
    }

    // 销毁 HLS 实例
    if (hls) {
      try {
        hls.stopLoad()
        hls.detachMedia()
        hls.destroy()
      } catch (e) {
        console.warn('销毁 HLS 实例失败:', e)
      } finally {
        hls = null
      }
    }

    // 重置视频元素
    if (videoRef.value) {
      try {
        videoRef.value.pause()
        videoRef.value.removeAttribute('src')
        videoRef.value.load()
      } catch (e) {
        console.warn('重置视频元素失败:', e)
      }
    }

    // 最后销毁 Video.js 播放器实例
    if (player.value) {
      try {
        await nextTick()
        player.value.dispose()
      } catch (e) {
        console.warn('销毁播放器实例失败:', e)
      } finally {
        player.value = null
      }
    }

    loading.value = false
  } catch (error) {
    console.error('销毁播放器失败:', error)
  }
}

// 监听源变化
watch(
  () => props.src,
  async (newSrc, oldSrc) => {
    if (!newSrc) {
      await destroyPlayer()
      return
    }

    if (newSrc !== oldSrc) {
      retryCount.value = 0
      // 确保完全销毁
      await destroyPlayer()
      // 等待 DOM 更新
      await nextTick()
      // 延迟初始化以确保清理完成
      setTimeout(async () => {
        if (videoRef.value) {
          // 再次检查确保组件仍然存在
          await initializePlayer()
        }
      }, 100)
    }
  },
  { immediate: true },
)

// 监听直播模式变化
watch(
  () => props.isLive,
  async (newIsLive, oldIsLive) => {
    if (newIsLive !== oldIsLive && props.src) {
      console.log('直播模式变化，重新初始化播放器')
      await destroyPlayer()
      await nextTick()
      await initializePlayer()
    }
  },
)

// 组件卸载前清理
onBeforeUnmount(async () => {
  await destroyPlayer()
})

// 暴露播放器实例和方法
defineExpose({
  player,
  videoRef,
  destroyPlayer,
  retry: handleRetry,
})
</script>

<style lang="less" scoped>
.video-player {
  width: 100%;
  height: 100%;
  position: relative;

  &.is-loading {
    .loading-overlay {
      display: flex;
    }
  }

  :deep(.video-js) {
    width: 100%;
    height: 100%;
    outline: none;

    // 自定义主题色
    &.vjs-theme-custom {
      --vjs-theme-color: #007aff;
    }

    // 直播样式优化
    &.vjs-live {
      .vjs-time-control {
        display: none;
      }

      .vjs-progress-control {
        display: none;
      }

      .vjs-live-display {
        display: flex;
      }
    }

    // 加载状态优化
    .vjs-loading-spinner {
      border-color: rgba(255, 255, 255, 0.7);
    }

    // 控制栏样式优化
    .vjs-control-bar {
      background-color: rgba(0, 0, 0, 0.7);
      backdrop-filter: blur(10px);
      transition: all 0.3s ease;
    }

    // 大播放按钮样式
    .vjs-big-play-button {
      background-color: rgba(0, 0, 0, 0.6);
      border-color: #fff;
      border-radius: 50%;
      width: 60px;
      height: 60px;
      line-height: 60px;
      transition: all 0.3s ease;

      &:hover {
        background-color: var(--vjs-theme-color, #007aff);
        transform: scale(1.1);
      }
    }

    // 音量控制优化
    .vjs-volume-panel {
      transition: all 0.3s ease;
    }

    // 进度条优化
    .vjs-progress-control {
      .vjs-progress-holder {
        margin: 0;
        height: 4px;
        transition: height 0.3s ease;
      }

      &:hover .vjs-progress-holder {
        height: 6px;
      }

      .vjs-play-progress {
        background-color: var(--vjs-theme-color, #007aff);

        &:before {
          color: var(--vjs-theme-color, #007aff);
        }
      }
    }

    // 播放速率菜单样式
    .vjs-playback-rate .vjs-menu-button {
      transition: all 0.3s ease;
    }
  }

  // 加载遮罩层
  .loading-overlay {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.3);
    display: none;
    justify-content: center;
    align-items: center;
    backdrop-filter: blur(2px);

    .loading-spinner {
      width: 40px;
      height: 40px;
      border: 3px solid rgba(255, 255, 255, 0.3);
      border-radius: 50%;
      border-top-color: #fff;
      animation: spin 1s ease-in-out infinite;
    }
  }

  // 错误遮罩层
  .error-overlay {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.7);
    display: flex;
    justify-content: center;
    align-items: center;
    backdrop-filter: blur(4px);

    .error-content {
      text-align: center;
      color: #fff;
      padding: 20px;

      .error-icon {
        font-size: 40px;
        display: block;
        margin-bottom: 10px;
      }

      .error-message {
        margin: 10px 0;
        font-size: 14px;
      }

      .retry-button {
        background: var(--vjs-theme-color, #007aff);
        border: none;
        color: #fff;
        padding: 8px 20px;
        border-radius: 4px;
        cursor: pointer;
        transition: all 0.3s ease;

        &:hover {
          background: darken(#007aff, 10%);
          transform: scale(1.05);
        }
      }
    }
  }
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}
</style>
